TOOL.Category = "Conna's Tools"
TOOL.Name = "#Contraption Saver Tool"
TOOL.Command = nil
TOOL.ConfigName = ""

// ConVars

TOOL.ClientConVar["Spawn_Frozen"] = "1"
TOOL.ClientConVar["Spawn_Selected"] = "0"
TOOL.ClientConVar["Sphere_Radius"] = "1"
TOOL.ClientConVar["Sphere"] = "0"
TOOL.ClientConVar["Description"] = ""
TOOL.ClientConVar["Directory"] = ""
TOOL.ClientConVar["File"] = ""

// Client

if (CLIENT) then
	// Tool
	
	language.Add("Tool_contraptionsaver_name", "Contraption Saver Tool")
	language.Add("Tool_contraptionsaver_desc", "Allows you to save and load contraptions")
	language.Add("Tool_contraptionsaver_0", "Left click to spawn a loaded contraption, Right click to select/deselect, Reload to clear selection")
	
	// Other
	
	language.Add("Cleanup_contraptions", "Contraptions")
	language.Add("Cleaned_contraptions", "Cleaned up all Contraptions")
	language.Add("Undone_contraption", "Undone Contraption")
end

// Cleanup

cleanup.Register("contraptions")

// Message

function TOOL:Message(Text)
	if (SERVER) then
		self:GetOwner():SendLua("GAMEMODE:AddNotify('"..Text.."', NOTIFY_GENERIC, 10)")
		self:GetOwner():SendLua("surface.PlaySound('ambient/water/drip"..math.random(1, 4)..".wav')")
	end
end

// Left click

function TOOL:LeftClick(Trace)
	if(CLIENT) then
		return true
	end

	if(!SERVER) then return false end
	
	// Player
	
	local Player = self:GetOwner()
	
	// Spawn
	
	ContraptionSaver.Commands.Spawn(Player)
	
	return true
end

// Think

function TOOL:Think()
	if (CLIENT) then return end
	
	// Player
	
	local Player = self:GetOwner()
	
	// Ghost entities
	
	if (ContraptionSaver.Players[Player].Ghosts) then
		if not (ContraptionSaver.SpawningContraption(Player)) then
			// Trace
			
			local Trace = Player:GetEyeTrace()
			
			// Table
			
			local Table = ContraptionSaver.Players[Player].Loaded.Table
			
			// Get first entity
			
			local Entity = ContraptionSaver.GetFirstEntry(Table.Entities)
			
			// Find lowest and convert entity positions
			
			if (Entity) then
				Entity = ContraptionSaver.GetHeadEntity(Entity, Table)
				
				if (Entity) then
					ContraptionSaver.ConvertEntityPositions(Player, Table, Trace)
				end
			end
			
			// Positions
			
			if (ContraptionSaver.Players[Player].Ghosts.Head) then
				local Valid = ContraptionSaver.Players[Player].Ghosts.Head[2]:IsValid()
				local Key = ContraptionSaver.Players[Player].Ghosts.Head[1]
				
				if (Valid) then
					if (Table.Entities[Key]) then
						ContraptionSaver.Players[Player].Ghosts.Head[2]:SetPos(Table.Entities[Key].Pos)
						ContraptionSaver.Players[Player].Ghosts.Head[2]:SetAngles(Table.Entities[Key].Angle)
						
						// Check if all entities are in the world
						
						for K, V in pairs(ContraptionSaver.Players[Player].Ghosts.Entities) do
							local Valid = V:IsValid()
							
							if (Valid) then
								local World = V:IsInWorld()
								local R, G, B, A = V:GetColor()
								
								if (World) then
									if (R == 255 and G == 100 and B == 100 and A == 100) then
										V:SetColor(255, 255, 255, 100)
									end
								else
									if (R == 255 and G == 255 and B == 255 and A == 100) then
										V:SetColor(255, 100, 100, 200)
									end
								end
							end
						end
					end
				end
			end
		end
	end
end

// Right click

function TOOL:RightClick(Trace)
	if (Trace.Entity) then
		if (!Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld) then
			return false
		end
	end
	
	// If it is the client then abort
	
	if(CLIENT) then
		return true
	end
	
	// Player
	
	local Player = self:GetOwner()
	
	// Sphere
	
	local Sphere = (self:GetClientNumber("Sphere") == 1)
	
	if (Sphere) then
		local Radius = self:GetClientNumber("Sphere_Radius")
		
		Radius = math.Clamp(Radius, 1, 512)
		
		local Entities = ents.FindInSphere(Trace.HitPos, Radius)
		
		for K, V in pairs(Entities) do
			local Class = V:GetClass()
			
			local Type = duplicator.FindEntityClass(Class)
			
			if (Type) then
				ContraptionSaver.Select(Player, V)
			end
		end
		
		self:Message("Entities in "..Radius.." radius have been selected!")
	else
		// Check
		
		local Index = Trace.Entity:EntIndex()
		
		if (ContraptionSaver.Players[Player].Entities[Index]) then ContraptionSaver.Deselect(Player, Trace.Entity) return false end
		
		ContraptionSaver.SelectConnected(Player, Trace.Entity)
	end

	return true
end

// Reload

function TOOL:Reload(Trace)
	// If it is the client then abort
	
	if(CLIENT) then
		return false
	end
	
	// Player
	
	local Player = self:GetOwner()
	
	// Clear
	
	local Count = ContraptionSaver.Clear(Player)
	
	if (Count) then
		ContraptionSaver.Message(Player, Count.." entities have been deselected!")
	end
	
	return false
end

// Holster

function TOOL:Holster()
	if (SERVER) then
		// Player
		
		local Player = self:GetOwner()
		
		// Unload
		
		ContraptionSaver.Unload(Player, true)
	end
end

// Client

if (CLIENT) then
	// ContraptionSaver
	
	ContraptionSaver = {}
	ContraptionSaver.List = {}
	ContraptionSaver.Page = "Main"
	ContraptionSaver.Directories = {}
	ContraptionSaver.Usermessage = {}
	ContraptionSaver.Actions = {
		{"Save", "contraptionsaver_Commands_Save", {"Main"}},
		{"Load Selected", "contraptionsaver_Commands_Load Selected", {"Main"}},
		{"Load", "contraptionsaver_Commands_Load", {"Main", "Contraption Listings"}},
		{"Delete", "contraptionsaver_Commands_Delete", {"Main", "Contraption Listings"}},
		{"Delete Directory", "contraptionsaver_Commands_Delete Directory", {"Contraption Listings"}},
		{"Download", "contraptionsaver_Commands_Download", {"Contraption Listings"}}
	}
	ContraptionSaver.Options = {
		["Spawn Frozen"] = "contraptionsaver_Spawn_Frozen",
		["Spawn Selected"] = "contraptionsaver_Spawn_Selected"
	}
	
	// Start
	
	function ContraptionSaver.Usermessage.Start(Message)
		ContraptionSaver.List = {}
		ContraptionSaver.Directories = {}
	end
	
	usermessage.Hook("ContraptionSaver.Start", ContraptionSaver.Usermessage.Start)
	
	// File
	
	function ContraptionSaver.Usermessage.File(Message)
		local File = Message:ReadString()
		
		local Explode = string.Explode("/", File) local Number = table.getn(Explode)
		
		local File = Explode[Number]
		
		local Directory = table.concat(Explode, "/", 1, Number - 1)
		
		// Check blank directory
		
		if (Directory != "") then
			table.insert(ContraptionSaver.Directories, Directory)
			
			ContraptionSaver.List[Directory] = ContraptionSaver.List[Directory] or {}
			
			table.insert(ContraptionSaver.List[Directory], File)
		end
	end
	
	usermessage.Hook("ContraptionSaver.File", ContraptionSaver.Usermessage.File)
	
	// Finish
	
	function ContraptionSaver.Usermessage.Finish(Message)
		// Sort them out alphabetically
		
		for K, V in pairs(ContraptionSaver.List) do
			table.sort(V, function(A, B) return A < B end)
		end
		
		// Sort out directories alphabetically
		
		table.sort(ContraptionSaver.Directories, function(A, B) return A < B end)
		
		// Rebuild the menu
		
		ContraptionSaver.Rebuild()
	end
	
	usermessage.Hook("ContraptionSaver.Finish", ContraptionSaver.Usermessage.Finish)
	
	// Message

	function ContraptionSaver.Message(Message)
		GAMEMODE:AddNotify(Message, NOTIFY_GENERIC, 10)
		
		surface.PlaySound('ambient/water/drip'..math.random(1, 4)..'.wav')
	end
	
	// Loaded
	
	function ContraptionSaver.Usermessage.Loaded(Message)
		ContraptionSaver.Loaded = {}
		
		// Data
		
		ContraptionSaver.Loaded["Date"] = Message:ReadString()
		ContraptionSaver.Loaded["Creator"] = Message:ReadString()
		ContraptionSaver.Loaded["Description"] = Message:ReadString()
		ContraptionSaver.Loaded["Version"] = Message:ReadString()
		ContraptionSaver.Loaded["Size"] = Message:ReadString()
		ContraptionSaver.Loaded["Entities"] = Message:ReadShort()
		ContraptionSaver.Loaded["Constraints"] = Message:ReadShort()
		
		// Rebuild
		
		ContraptionSaver.Rebuild()
	end
	
	usermessage.Hook("ContraptionSaver.Loaded", ContraptionSaver.Usermessage.Loaded)
	
	// Unload
	
	function ContraptionSaver.Usermessage.Unload(Message)
		ContraptionSaver.Loaded = false
		
		// Rebuild
		
		ContraptionSaver.Rebuild()
	end
	
	usermessage.Hook("ContraptionSaver.Unload", ContraptionSaver.Usermessage.Unload)
	
	// Menu
	
	function ContraptionSaver.Menu(Player, Command, Args)
		Args = table.concat(Args, " ")
		
		ContraptionSaver.Page = Args
		
		ContraptionSaver.Rebuild()
	end
	
	concommand.Add("contraptionsaver_Menu", ContraptionSaver.Menu)
	
	// Download
	
	function ContraptionSaver.Usermessage.Download(Message)
		local Type = Message:ReadShort()
		
		if (Type == 1) then
			local Main = Message:ReadString()
			
			ContraptionSaver.Downloading = {}
			ContraptionSaver.Downloading.String = ""
			ContraptionSaver.Downloading.Path = Main.."/"
		elseif (Type == 2) then
			local Dir = Message:ReadString()
			
			ContraptionSaver.Downloading.Path = ContraptionSaver.Downloading.Path..Dir
		elseif (Type == 3) then
			local File = Message:ReadString()
			
			ContraptionSaver.Downloading.Path = ContraptionSaver.Downloading.Path..File
		elseif (Type == 4) then
			local Size = Message:ReadString()
			
			ContraptionSaver.Downloading.Size = tonumber(Size)
			ContraptionSaver.Downloading.Done = 0
		elseif (Type == 5) then
			local String = Message:ReadString()
			
			ContraptionSaver.Downloading.String = ContraptionSaver.Downloading.String..String
			
			ContraptionSaver.Downloading.Done = string.len(ContraptionSaver.Downloading.String)
			
			local Percentage = (100 / ContraptionSaver.Downloading.Size) * ContraptionSaver.Downloading.Done
			
			Percentage = math.floor(Percentage)
			
			Percentage = math.Clamp(Percentage, 0, 100)
			
			ContraptionSaver.Downloading.Percentage = Percentage
		elseif (Type == 6) then
			file.Write(ContraptionSaver.Downloading.Path, ContraptionSaver.Downloading.String)
			
			ContraptionSaver.Message(ContraptionSaver.Downloading.Path.." has been downloaded!")
			
			// Finish
			
			LocalPlayer():ConCommand("contraptionsaver_Commands_Finish Downloading\n")
			
			// End downloading
			
			ContraptionSaver.Downloading = false
		end
	end
	
	usermessage.Hook("ContraptionSaver.Download", ContraptionSaver.Usermessage.Download)
	
	// HUDPaint
	
	function ContraptionSaver.HUDPaint()
		if (ContraptionSaver.Downloading and ContraptionSaver.Downloading.Percentage) then
			surface.SetFont("Default")
			
			local Text = "Downloading: "..ContraptionSaver.Downloading.Percentage.."%"
			
			local W, H = surface.GetTextSize(Text)
			
			W = W + 16
			
			local X = (ScrW() / 2) - (W / 2)
			local Y = (ScrH() / 2) + 16
			
			draw.RoundedBox(4, X, Y, W, 32, Color(0, 0, 0, 100))
			
			draw.RoundedBox(4, X, Y + 40, W, 16, Color(100, 100, 100, 100))
			
			local Size = (W / ContraptionSaver.Downloading.Size) * ContraptionSaver.Downloading.Done
			
			Size = math.floor(Size)
			
			Size = math.Clamp(Size, 4, W)
			
			draw.RoundedBox(4, X, Y + 40, Size, 16, Color(255, 50, 50, 200))
			
			draw.SimpleText(Text, "Default", X + (W / 2), Y + 16, Color(255, 255, 255, 255), 1, 1) 
		end
	end
	
	hook.Add("HUDPaint", "ContraptionSaver.HUDPaint", ContraptionSaver.HUDPaint)
	
	// Rebuild
	
	function ContraptionSaver.Rebuild()
		local Panel = GetControlPanel("contraptionsaver")
		
		if not (Panel) then return end
		
		Panel:ClearControls()
		Panel:AddHeader()
		Panel:AddDefaultControls()
		
		// Header
		
		Panel:AddControl("Header", {Text = "#Tool_contraptionsaver_name", Description = "#Tool_contraptionsaver_desc"})
		
		// Loaded contraption
		
		if (ContraptionSaver.Loaded) then
			Panel:AddControl("Label", {Text = "Date: "..ContraptionSaver.Loaded["Date"]})
			Panel:AddControl("Label", {Text = "Size: "..ContraptionSaver.Loaded["Size"].." KB"})
			Panel:AddControl("Label", {Text = "Creator: "..ContraptionSaver.Loaded["Creator"]})
			Panel:AddControl("Label", {Text = "Version: "..ContraptionSaver.Loaded["Version"]})
			Panel:AddControl("Label", {Text = "Entities: "..ContraptionSaver.Loaded["Entities"]})
			Panel:AddControl("Label", {Text = "Constraints: "..ContraptionSaver.Loaded["Constraints"]})
			Panel:AddControl("Label", {Text = "Description: "..ContraptionSaver.Loaded["Description"]})
			
			// Button
			
			Panel:AddControl("Button", {Label = "#Spawn", Text = "OK", Command = "contraptionsaver_Commands_Spawn"})
			
			// Unload
			
			Panel:AddControl("Label", {Text = "Unload"})
			
			Panel:AddControl("Button", {Label = "#Unload", Text = "OK", Command = "contraptionsaver_Commands_Unload"})
			
			// Spawns settings
			
			Panel:AddControl("Label", {Text = "Spawn Settings"})
			
			for K, V in pairs(ContraptionSaver.Options) do
				Panel:AddControl("CheckBox", {Label = "#"..K, Command = V})
			end
		else
			// Back
			
			if (ContraptionSaver.Page != "Main") then
				Panel:AddControl("Button", {Label = "#Back", Text = "OK", Command = "contraptionsaver_Menu Main"})
			end
			
			// File settings
			
			if (ContraptionSaver.Page == "Main") then
				Panel:AddControl("Label", {Text = "File Settings"})
				
				Panel:AddControl("TextBox", {Label = "Description", MaxLength = "75", Command = "contraptionsaver_Description"})
				Panel:AddControl("TextBox", {Label = "Directory", MaxLength = "75", Command = "contraptionsaver_Directory"})
				Panel:AddControl("TextBox", {Label = "File", MaxLength = "75", Command = "contraptionsaver_File"})
			end
			
			// Contraption actions
			
			if (ContraptionSaver.Page == "Main" or ContraptionSaver.Page == "Contraption Listings") then
				Panel:AddControl("Label", {Text = "File Actions"})
				
				table.sort(ContraptionSaver.Actions, function(A, B) return string.len(A[1]) < string.len(B[1]) end)
				
				for K, V in pairs(ContraptionSaver.Actions) do
					if (table.HasValue(V[3], ContraptionSaver.Page)) then
						Panel:AddControl("Button", {Label = "#"..V[1], Text = "OK", Command = V[2]})
					end
				end
			end
			
			// Pages
			
			if (ContraptionSaver.Page == "Main") then
				Panel:AddControl("Label", {Text = "Pages"})
			end
				
			// Spawn settings
			
			if (ContraptionSaver.Page == "Main") then
				Panel:AddControl("Button", {Label = "#Spawn Settings", Text = "Open", Command = "contraptionsaver_Menu Spawn Settings"})
			end
			
			if (ContraptionSaver.Page == "Spawn Settings") then
				Panel:AddControl("Label", {Text = "Spawn Settings"})
				
				for K, V in pairs(ContraptionSaver.Options) do
					Panel:AddControl("CheckBox", {Label = "#"..K, Command = V})
				end
			end
			
			// Selection settings
			
			if (ContraptionSaver.Page == "Main") then
				Panel:AddControl("Button", {Label = "#Selection Settings", Text = "Open", Command = "contraptionsaver_Menu Selection Settings"})
			end
			
			if (ContraptionSaver.Page == "Selection Settings") then
				Panel:AddControl("CheckBox", {Label = "#Sphere Select", Command = "contraptionsaver_Sphere"})
				
				Panel:AddControl("Slider", {Label = "Radius", Type = "Integer", Min = 1, Max = 512, Command = "contraptionsaver_Sphere_Radius"})
			end
			
			// Contraption listings
			
			if (ContraptionSaver.Page == "Main") then
				Panel:AddControl("Button", {Label = "#Contraption Listings", Text = "Open", Command = "contraptionsaver_Menu Contraption Listings"})
			end
			
			if (ContraptionSaver.Page == "Contraption Listings") then
				Panel:AddControl("Label", {Text = "Contraption Listings"})
				
				Panel:AddControl("Button", {Label = "#Generate Directories List", Text = "Generate", Command = "contraptionsaver_Commands_Generate"})
				
				// List
				
				local Params = {}
				
				Params.Height = 200
				Params.Label = "Directories"
				Params.Options = {}
				
				for K, V in pairs(ContraptionSaver.Directories) do
					Params.Options[V] = {}
					Params.Options[V].contraptionsaver_Directory = V
				end
				
				Panel:AddControl("ListBox", Params)
				
				// Contraptions
				
				Panel:AddControl("Button", {Label = "#Generate Contraptions List", Text = "Generate", Command = "contraptionsaver_Rebuild"})
				
				// Directory
				
				local Directory = LocalPlayer():GetInfo("contraptionsaver_Directory")
				
				if (ContraptionSaver.List[Directory]) then
					local Params = {}
					
					Params.Height = 200
					Params.Label = "Contraptions in "..Directory
					Params.Options = {}
					
					for K, V in pairs(ContraptionSaver.List[Directory]) do
						Params.Options[V] = {}
						Params.Options[V].contraptionsaver_File = V
						Params.Options[V].contraptionsaver_Directory = Directory
					end
					
					Panel:AddControl("ListBox", Params)
				end
			end
		end
	end
	
	concommand.Add("contraptionsaver_Rebuild", ContraptionSaver.Rebuild)
end

// Deploy

function TOOL:Deploy()
	if (SERVER) then
		ContraptionSaver.GenerateDirectories(Player)
	end
end

// Build CPanel

function TOOL.BuildCPanel(Panel)
end